package carte.element;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Random;

import controller.Evolution;

import carte.Cellule;
import carte.terrain.Land;

/**
 * Chaque case, en plus de son type classique peut comprendre un fluide dans une
 * certaine quantité
 * 
 * @author flagadur
 * 
 */
abstract public class Fluid implements Cloneable, Evolution {

	protected String type;
	// couleur du fluide
	protected Color texture;
	// quantité du fluide (l'altitude de la case augmente d'autant)
	protected int quantity;
	// Le mana est un fluide magique qui se comporte de façon exactement inverse
	// à un fluide classique
	// c'est à dire qu'il monte vers le haut à la place de descendre
	protected boolean isMist;
	
	// maximum de fluide par case en cas de brume
	public static final int MAX_MIST_QUANTITY = 3;
	// la brume s'evapore au fur et a mesure, voici la probabilite de dissipation
	public static final float DISSIPATION_RATE = 0.1f;
	
	protected static Random rand = new Random();

	public Fluid(Color texture, int quantity, String type, boolean isMist) {
		this.texture = texture;
		this.quantity = quantity;
		this.type = type;
		this.isMist = isMist;
	}

	// Le fluide va tenter de s'écouler dans les cases les plus basses
	public void evolve(ArrayList<Cellule> oldEnv, ArrayList<Cellule> newEnv) {
		//on va faire evoluer le fluide de la case s'il y a toujours du fluide dessus
		if(newEnv.get(0).containFluid() && oldEnv.get(0).containFluid())
		{
			if (isMist) {
				evolutionMist(oldEnv, newEnv);
			} else {
				
				evolutionNormal(oldEnv, newEnv);
			}
		}
	}

	private void evolutionNormal(ArrayList<Cellule> oldEnv,
		ArrayList<Cellule> newEnv) {
		// on ne va conserver les cases autour qui ont une hauteur plus basse
		// dans une arraylist
		ArrayList<Cellule> lowCells = new ArrayList<Cellule>();
		int altitude = oldEnv.get(0).getAltitude();
		int quantityOfFluid = oldEnv.get(0).getFluid().getQuantity();
		int initialQuantity = quantityOfFluid;
		int quantityToTranfer = 0;
		for (int index = 1; index < oldEnv.size(); index++) {
			if (newEnv.get(index).computeElevation() < oldEnv.get(0)
					.computeElevation()) {
				lowCells.add(newEnv.get(index));
			}
		}
		if (lowCells.size() > 0) {
			// on trie la liste des cellules
			Collections.sort(lowCells);
			do {
				// on va donner tout le fluid qu'on peut à la case la plus basse
				// en ne donnant pas plus une plus grande quantité de ce que
				// l'on a
				// et en conservant une elevation au moins egale
				quantityToTranfer = Math.min(
						((oldEnv.get(0).computeElevation() - lowCells.get(0)
								.computeElevation()) / 2) +1, quantityOfFluid);
				// on verifie que le transfert est bien possible
				if (lowCells.get(0).addFluid(type, quantityToTranfer)) {
					// dans ce cas on enleve bien la quantite demandee
					quantityOfFluid -= quantityToTranfer;
				}
				// dans tous les cas on enleve cette cellule de notre arraylist
				lowCells.remove(0);
				// ensuite on enleve toutes les cellules qui sont maintenant
				// au-dessus de notre case
				while (lowCells.size() > 0
						&& (altitude + quantityOfFluid) <= lowCells.get(0)
								.computeElevation()) {
					lowCells.remove(0);
				}
			} while (lowCells.size() > 0 && quantityOfFluid >= 2);
			// a ce moment il faut actualiser la cellule actuelle
			newEnv.get(0).removeFluid(initialQuantity - quantityOfFluid);
		}
	}

	private void evolutionMist(ArrayList<Cellule> oldEnv,
			ArrayList<Cellule> newEnv) {
		// on ne va conserver les cases autour qui ont une hauteur plus haute
		// dans une arraylist
		ArrayList<Cellule> highCells = new ArrayList<Cellule>();
		int altitude = oldEnv.get(0).getAltitude();
		int quantityOfFluid = oldEnv.get(0).getFluid().getQuantity();
		int initialQuantity = quantityOfFluid;
		
		for (int index = 1; index < oldEnv.size(); index++) {
			if (newEnv.get(index).getAltitude() > oldEnv.get(0)
					.getAltitude()) {
				highCells.add(newEnv.get(index));
			}
		}
		if (highCells.size() > 0) {
			// on trie la liste des cellules dans l'ordre croissant, on
			// considère que le brouillard
			// va d'abord sur les cases de plus faible hauteur mais qui sont
			// au-dessus de notre case
			Collections.sort(highCells);
			for(Cellule cell : highCells){
				//on essaye de lui ajouter 1 quantite de brume
				if(cell.addFluid(type, 1)){
					quantityOfFluid -= 1;
				}
				//si on n'a plus de fluide on quitte la boucle
				if(quantityOfFluid == 0){
					break;
				}
			}
			// a ce moment il faut actualiser la cellule actuelle
			newEnv.get(0).removeFluid(initialQuantity - quantityOfFluid);
		}
		//ensuite il y a une probabilité que la brume se dissipe
		if(quantityOfFluid > 0){
			if(rand.nextFloat() < DISSIPATION_RATE){
				newEnv.get(0).removeFluid(1);
			}
		}
	}

	// on peut cloner le fluide
	public Object clone() {
		Fluid fluid = null;
		// On récupère l'instance à renvoyer par l'appel de la
		// méthode super.clone()
		try {
			fluid = (Fluid) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		// on renvoie le clone
		return fluid;
	}

	// ajouter du fluide
	// si c'est du mist on verifie que l'on ne depasse pas la quantite max sinon
	// on n'ajoute rien et on retourne false
	public boolean addFluid(int quantity) {
		if (isMist) {
			if (this.quantity + quantity > MAX_MIST_QUANTITY) {
				return false;
			}
		}
		this.quantity += quantity;
		return true;
	}

	// retirer du fluide à ne jamais utiliser directement
	// utiliser celui de la cellule
	// return true si la case est videe de tout son fluide
	public boolean removeFluid(int quantity) {
		if (quantity >= this.quantity) {
			this.quantity = 0;
			return true;
		} else {
			this.quantity -= quantity;
			return false;
		}
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public Color getTexture() {
		return texture;
	}

	public boolean isMana() {
		return isMist;
	}

	public String getType() {
		return type;
	}
}
